package com.beardedhen.harmonybootstrap;

import static ohos.agp.components.ComponentState.COMPONENT_STATE_DISABLED;
import static ohos.agp.components.ComponentState.COMPONENT_STATE_FOCUSED;
import static ohos.agp.components.ComponentState.COMPONENT_STATE_PRESSED;
import static ohos.agp.components.ComponentState.COMPONENT_STATE_SELECTED;

import com.beardedhen.harmonybootstrap.api.attributes.BootstrapBrand;
import com.beardedhen.harmonybootstrap.api.attributes.ViewGroupPosition;
import com.beardedhen.harmonybootstrap.api.defaults.DefaultBootstrapBrand;
import com.beardedhen.harmonybootstrap.api.defaults.ExpandDirection;
import com.beardedhen.harmonybootstrap.utils.ColorUtils;
import com.beardedhen.harmonybootstrap.utils.DimenUtils;
import com.beardedhen.harmonybootstrap.utils.ResUtil;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.ComponentState;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.render.Texture;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;

import java.io.IOException;

/**
 * Provides a factory for generating Drawables which are used as the backgrounds for Bootstrap Views.
 * This avoids needing to define hundreds of XML resource files.
 */
public class BootstrapDrawableFactory {

    private static final PixelFormat BITMAP_CONFIG = PixelFormat.ARGB_8888;

    public static Element createBadgeDrawable(Context context, BootstrapBrand brand, int width,
                                              int height, String badgeText, boolean insideAnObject) {
        if (badgeText == null) {
            return null;
        }
        else {

            Paint badgePaint = new Paint();
            Rect canvasBounds = new Rect();
            Paint badgeTextPaint = new Paint();

            badgeTextPaint.setTextAlign(TextAlignment.CENTER);
            badgeTextPaint.setTextSize((int) (height * 0.7));

            if (insideAnObject) {

                badgePaint.setColor(new Color(brand.defaultTextColor(context)));
                badgeTextPaint.setColor(new Color(brand.defaultFill(context)));
            }
            else {

                badgePaint.setColor(new Color(brand.defaultFill(context)));
                badgeTextPaint.setColor(new Color(brand.defaultTextColor(context)));

            }

            int rectLength = (int) badgeTextPaint.measureText(badgeText);

            PixelMap pixelMap;
            PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();

            initializationOptions.size = new Size(width+rectLength,height);
            initializationOptions.pixelFormat = BITMAP_CONFIG;
            pixelMap = PixelMap.create(initializationOptions);

            Canvas canvas = new Canvas(new Texture(pixelMap));


            canvasBounds = canvas.getLocalClipBounds();

            if (canvasBounds == null) {
                canvasBounds = new Rect(0,0, width+rectLength, height);
            }

            int firstCircleDx = canvasBounds.left + canvasBounds.getHeight() / 2;
            int circleDy = canvasBounds.getHeight() / 2;
            int circleRadius = canvasBounds.getHeight() / 2;

            int secondCircleDx = firstCircleDx + rectLength;

            RectFloat rect = new RectFloat();
            rect.left = firstCircleDx;
            rect.top = 0;
            rect.right = rect.left + rectLength;
            rect.bottom = canvasBounds.getHeight();

            canvas.drawCircle(firstCircleDx, circleDy, circleRadius, badgePaint);
            canvas.drawRect(rect, badgePaint);
            canvas.drawCircle(secondCircleDx, circleDy, circleRadius, badgePaint);
            canvas.drawText(badgeTextPaint, badgeText, ((float) canvasBounds.getWidth() / 2),
                ((float) canvasBounds.getHeight() / 2 - ((badgeTextPaint.descent() + badgeTextPaint.ascent()) / 2)));

            return new PixelMapElement( pixelMap);
        }
    }

    static Element bootstrapAlert(Context context,
                                  BootstrapBrand bootstrapBrand) {
        ShapeElement disabledGd = new ShapeElement();
        int strokeWidth = ResUtil.getIntDimen(context,ResourceTable.Float_bootstrap_alert_stroke_width);
        disabledGd.setRgbColor(
            RgbColor.fromRgbaInt(ColorUtils.increaseOpacityFromInt(context, bootstrapBrand.getColor(),40)));
        disabledGd.setCornerRadius(6);
        disabledGd.setStroke(strokeWidth,
            RgbColor.fromRgbaInt(ColorUtils.increaseOpacityFromInt(context, bootstrapBrand.getColor(), 70)));
        return disabledGd;
    }

    /**
     * Generates a colorstatelist for a bootstrap button
     *
     * @param context the current context
     * @param outline whether the button is outlined
     * @param brand   the button brand
     * @return the color state list
     */
    static int[] bootstrapButtonText(Context context, boolean outline, BootstrapBrand brand) {

        int defaultColor = outline ? brand.defaultFill(context) : brand.defaultTextColor(context);
        int activeColor = outline ? ColorUtils.resolveColor(ResourceTable.Color_white, context) : brand.activeTextColor(context);
        int disabledColor = outline ? brand.disabledFill(context) : brand.disabledTextColor(context);

        if (outline && brand instanceof DefaultBootstrapBrand) { // special case
            DefaultBootstrapBrand db = (DefaultBootstrapBrand) brand;

            if (db == DefaultBootstrapBrand.SECONDARY) {
                defaultColor = ColorUtils.resolveColor(ResourceTable.Color_bootstrap_brand_secondary_border, context);
                disabledColor = defaultColor;
            }
        }
        return new int[] {defaultColor,activeColor,disabledColor};
    }

    static StateElement bootstrapAlertCloseIcon(Context context, int width, int height, int inset) {

        StateElement stateListDrawable = new StateElement();

        int defaultColor = ColorUtils.resolveColor(ResourceTable.Color_bootstrap_alert_cross_default, context);
        int activeColor = ColorUtils.resolveColor(ResourceTable.Color_bootstrap_gray, context);
        int disabledColor = ColorUtils.resolveColor(ResourceTable.Color_bootstrap_alert_cross_default, context);

        stateListDrawable.addState(new int[]{ComponentState.COMPONENT_STATE_CHECKED}, createCloseCrossIcon(context, width, height, activeColor, inset));
        stateListDrawable.addState(new int[]{ComponentState.COMPONENT_STATE_FOCUSED}, createCloseCrossIcon(context, width, height, activeColor, inset));
        stateListDrawable.addState(new int[]{ComponentState.COMPONENT_STATE_PRESSED}, createCloseCrossIcon(context, width, height, activeColor, inset));
        stateListDrawable.addState(new int[]{ComponentState.COMPONENT_STATE_SELECTED}, createCloseCrossIcon(context, width, height, activeColor, inset));
        stateListDrawable.addState(new int[]{ComponentState.COMPONENT_STATE_DISABLED}, createCloseCrossIcon(context, width, height, disabledColor, inset));
        stateListDrawable.addState(new int[]{}, createCloseCrossIcon(context, width, height, defaultColor, inset));
        return stateListDrawable;
    }

    private static PixelMapElement createCloseCrossIcon(Context context, int width, int height, int color, int inset) {

        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
        initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
        initializationOptions.size = new Size(width + inset * 2, height + inset * 2);
        PixelMap canvasBitmap = PixelMap.create(initializationOptions);
        Canvas canvas = new Canvas();
        canvas.setTexture(new Texture(canvasBitmap));
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILLANDSTROKE_STYLE);
        paint.setStrokeWidth(3);
        paint.setColor(new Color(color));
        paint.setAntiAlias(true);
        Path path = new Path();
        path.setFillType(Path.FillType.EVEN_ODD);
        path.moveTo(inset, inset);
        path.lineTo(width + inset, height + inset);
        path.moveTo(width + inset, inset);
        path.lineTo(inset, height + inset);
        path.close();
        canvas.drawPath(path, paint);
        return new PixelMapElement(canvasBitmap);

    }

    static Element bootstrapWell(int backgroundColor, int cornerRadius, int strokeWidth, int strokeColor) {
        ShapeElement background = new ShapeElement();
        background.setRgbColor(RgbColor.fromArgbInt(backgroundColor));
        background.setCornerRadius(cornerRadius);
        background.setStroke(strokeWidth, RgbColor.fromArgbInt(strokeColor));
        return background;
    }


    static Element bootstrapLabel(Context context,
                                  BootstrapBrand bootstrapBrand,
                                  boolean rounded,
                                  float height) throws NotExistException, WrongTypeException, IOException {

        int cornerRadius = (int) DimenUtils.pixelsFromDpResource(context, ResourceTable.Float_bootstrap_default_corner_radius);

        ShapeElement drawable = new ShapeElement();
        drawable.setRgbColor(RgbColor.fromArgbInt(bootstrapBrand.defaultFill(context)));
        drawable.setCornerRadius(rounded ? height / 2 : cornerRadius);
        return drawable;
    }

    static Element bootstrapEditText(Context context,
                                     BootstrapBrand bootstrapBrand,
                                     int strokeWidth,
                                     float cornerRadius,
                                     boolean rounded) {

        StateElement drawable = new StateElement();

        ShapeElement activeDrawable = new ShapeElement();
        ShapeElement disabledDrawable = new ShapeElement();
        ShapeElement defaultDrawable = new ShapeElement();

        activeDrawable.setRgbColor(RgbColor.fromArgbInt(ResUtil.getColor(context,ResourceTable.Color_white)));
        disabledDrawable.setRgbColor(RgbColor.fromArgbInt(ResUtil.getColor(context,ResourceTable.Color_white)));
        defaultDrawable.setRgbColor(RgbColor.fromArgbInt(ResUtil.getColor(context,ResourceTable.Color_white)));

        if(rounded){
            activeDrawable.setCornerRadius(cornerRadius);
            disabledDrawable.setCornerRadius(cornerRadius);
            defaultDrawable.setCornerRadius(cornerRadius);
        }

        int defaultBorder = ResUtil.getColor(context,ResourceTable.Color_bootstrap_brand_secondary_border);
        int disabledBorder = ResUtil.getColor(context,ResourceTable.Color_bootstrap_edittext_disabled);

        activeDrawable.setStroke(strokeWidth,RgbColor.fromArgbInt(bootstrapBrand.defaultFill(context)));
        disabledDrawable.setStroke(strokeWidth,RgbColor.fromArgbInt(disabledBorder));
        defaultDrawable.setStroke(strokeWidth,RgbColor.fromArgbInt(defaultBorder));

        drawable.addState(new int[]{COMPONENT_STATE_FOCUSED},activeDrawable);
        drawable.addState(new int[]{COMPONENT_STATE_DISABLED},disabledDrawable);
        drawable.addState(new int[]{},defaultDrawable);

        return drawable;
    }

    static Element bootstrapThumbnail(Context context,
                                      BootstrapBrand bootstrapBrand,
                                      int outerBorderWidth,
                                      int bg,
                                      boolean rounded) {

        ShapeElement drawable = new ShapeElement();
        drawable.setShape(ShapeElement.RECTANGLE);
        drawable.setRgbColor(RgbColor.fromArgbInt(bg));

        drawable.setStroke(outerBorderWidth, RgbColor.fromArgbInt(bootstrapBrand.defaultEdge(context)));

        float r = ResUtil.getDimen(context,ResourceTable.Float_bthumbnail_rounded_corner);

        if (rounded) {
            drawable.setCornerRadiiArray(new float[]{r, r, r, r, r, r, r, r});
        }

        return drawable;
    }

    static Element bootstrapButton(Context context, BootstrapBrand brand, int strokeWidth, int cornerRadius, ViewGroupPosition position, boolean showOutline, boolean rounded) {
        ShapeElement defaultGd = new ShapeElement();
        ShapeElement activeGd = new ShapeElement();
        ShapeElement disabledGd = new ShapeElement();
        defaultGd.setRgbColor(RgbColor.fromArgbInt((showOutline ? ResUtil.getColor(context, ResourceTable.Color_bootstrap_brand_transparent) : brand.defaultFill(context))));
        activeGd.setRgbColor(RgbColor.fromArgbInt(brand.activeFill(context)));
        disabledGd.setRgbColor(RgbColor.fromArgbInt((showOutline ? ResUtil.getColor(context, ResourceTable.Color_bootstrap_brand_transparent) : brand.disabledFill(context))));
        defaultGd.setStroke(strokeWidth, RgbColor.fromArgbInt(brand.defaultFill(context)));

        activeGd.setStroke(strokeWidth, RgbColor.fromArgbInt(brand.defaultFill(context)));
        disabledGd.setStroke(strokeWidth, RgbColor.fromArgbInt(brand.disabledEdge(context)));

        if (showOutline && brand instanceof DefaultBootstrapBrand) {
            DefaultBootstrapBrand db = (DefaultBootstrapBrand) brand;

            if (db == DefaultBootstrapBrand.SECONDARY) {
                int color = ColorUtils.resolveColor(ResourceTable.Color_bootstrap_brand_secondary_border, context);

                defaultGd.setStroke(strokeWidth, RgbColor.fromArgbInt(color));
                activeGd.setStroke(strokeWidth, RgbColor.fromArgbInt(color));
                disabledGd.setStroke(strokeWidth, RgbColor.fromArgbInt(color));
            }
        }
        setupDrawableCorners(position, rounded, cornerRadius, defaultGd, activeGd, disabledGd);
        return setupStateDrawable(position, strokeWidth, defaultGd, activeGd, disabledGd);
    }

    private static void setupDrawableCorners(ViewGroupPosition position, boolean rounded, int r,
                                             ShapeElement defaultGd, ShapeElement activeGd, ShapeElement disabledGd) {
        if (rounded) {
            if (position == ViewGroupPosition.SOLO) {
                defaultGd.setCornerRadius(r);
                activeGd.setCornerRadius(r);
                disabledGd.setCornerRadius(r);
            }
            else {
                float[] radii; // X/Y pairs for top-left, top-right, bottom-right, bottom-left.

                switch (position) {
                    case MIDDLE_HORI:
                        radii = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
                        break;
                    case MIDDLE_VERT:
                        radii = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
                        break;
                    case TOP:
                        radii = new float[]{r, r, r, r, 0, 0, 0, 0};
                        break;
                    case BOTTOM:
                        radii = new float[]{0, 0, 0, 0, r, r, r, r};
                        break;
                    case START:
                        radii = new float[]{r, r, 0, 0, 0, 0, r, r,};
                        break;
                    case END:
                        radii = new float[]{0, 0, r, r, r, r, 0, 0};
                        break;
                    default:
                        radii = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
                        break;
                }

                defaultGd.setCornerRadiiArray(radii);
                activeGd.setCornerRadiiArray(radii);
                disabledGd.setCornerRadiiArray(radii);
            }
        }
    }

    private static StateElement setupStateDrawable(ViewGroupPosition position, int strokeWidth, ShapeElement defaultGd, ShapeElement activeGd, ShapeElement disabledGd) {
        StateElement stateListDrawable = new StateElement();
        stateListDrawable.addState(new int[]{COMPONENT_STATE_SELECTED},activeGd);
        stateListDrawable.addState(new int[]{COMPONENT_STATE_PRESSED},activeGd);
        stateListDrawable.addState(new int[]{COMPONENT_STATE_FOCUSED},activeGd);
        stateListDrawable.addState(new int[]{COMPONENT_STATE_DISABLED},disabledGd);
        stateListDrawable.addState(new int[]{}, defaultGd);
        return stateListDrawable;
    }

    static StateElement bootstrapDropDownArrow(Context context, int width, int height, ExpandDirection expandDirection, boolean outline, BootstrapBrand brand) {
        StateElement stateElement = new StateElement();
        int defaultColor = outline ? brand.defaultFill(context) : brand.defaultTextColor(context);
        stateElement.addState(new int[]{}, createArrowIcon(context, width, height, defaultColor, expandDirection));
        return stateElement;
    }

    private static Element createArrowIcon(Context context, int width, int height, int color, ExpandDirection expandDirection){
        PixelMap pixelMap;
        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();

        initializationOptions.size = new Size(width,height);
        initializationOptions.pixelFormat = BITMAP_CONFIG;
        pixelMap = PixelMap.create(initializationOptions);
        Canvas canvas = new Canvas(new Texture(pixelMap));
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILLANDSTROKE_STYLE);
        paint.setStrokeWidth(1);
        paint.setColor(new Color(color));
        paint.setAntiAlias(true);
        Path path = new Path();
        path.setFillType(Path.FillType.EVEN_ODD);
        switch (expandDirection) {
            case UP:
                path.moveTo(0, (height / 3) * 2);
                path.lineTo(width, (height / 3) * 2);
                path.lineTo(width / 2, height / 3);
                path.lineTo(0, (height / 3) * 2);
                break;
            case DOWN:
                path.moveTo(0, height / 3);
                path.lineTo(width, height / 3);
                path.lineTo(width / 2, (height / 3) * 2);
                path.lineTo(0, height / 3);
                break;
        }
        path.close();
        canvas.drawPath(path,paint);
        return new PixelMapElement(pixelMap);
    }

}
